/*
MIT License

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/loom
*/

#include "simodo/variable/json/LexicalParametersLoader.h"
#include "simodo/variable/json/parser/JsonRdp.h"

namespace simodo::variable
{
    namespace
    {
        inout::LexemeType loadLexemeType(const std::u16string & type_string)
        {
            inout::LexemeType type;

            if (type_string == u"Compound")
                type = inout::LexemeType::Compound;
            else if (type_string == u"Empty")
                type = inout::LexemeType::Empty;
            else if (type_string == u"Punctuation")
                type = inout::LexemeType::Punctuation;
            else if (type_string == u"Id")
                type = inout::LexemeType::Id;
            else if (type_string == u"Annotation")
                type = inout::LexemeType::Annotation;
            else if (type_string == u"Number")
                type = inout::LexemeType::Number;
            else if (type_string == u"Comment")
                type = inout::LexemeType::Comment;
            else
                type = inout::LexemeType::Error;

            return type;
        }

        bool loadMarkups(const Value & value, std::vector<inout::MarkupSymbol> & markups)
        {
            if (value.type() != ValueType::Array)
                return false;

            const std::shared_ptr<Array> array = value.getArray();

            for(const Value & v : array->values()) {
                if (v.type() != ValueType::Object)
                    return false;

                const std::shared_ptr<Object> obj = v.getObject();

                inout::MarkupSymbol markup;

                if (obj->exists(u"start"))
                    markup.start = obj->find(u"start").getString();

                if (obj->exists(u"end"))
                    markup.end = obj->find(u"end").getString();

                if (obj->exists(u"ignore_sign"))
                    markup.ignore_sign = obj->find(u"ignore_sign").getString();

                std::u16string type_string;
                if (obj->exists(u"type"))
                    type_string = obj->find(u"type").getString();

                if (type_string.empty())
                    return false;

                markup.type = loadLexemeType(type_string);

                markups.push_back(markup);
            }

            return true;
        }

        bool loadMasks(const Value & value, std::vector<inout::NumberMask> & masks)
        {
            if (value.type() != ValueType::Array)
                return false;

            const std::shared_ptr<Array> array = value.getArray();

            for(const Value & v : array->values())
            {
                if (v.type() != ValueType::Object)
                    return false;

                const std::shared_ptr<Object> obj = v.getObject();

                inout::NumberMask mask;

                if (obj->exists(u"chars"))
                    mask.chars = obj->find(u"chars").getString();

                std::u16string type_string;
                if (obj->exists(u"type"))
                    type_string = obj->find(u"type").getString();

                if (type_string.empty())
                    return false;

                mask.type = loadLexemeType(type_string);

                if (obj->exists(u"system"))
                {
                    int system_number = obj->find(u"system").getInt();

                    if (system_number < 2 || system_number > 16)
                        return false;

                    mask.system = static_cast<inout::number_system_t>(system_number);
                }

                /// \todo cppcheck: error: Uninitialized struct member: mask.system [uninitStructMember]
                masks.push_back(mask);
            }

            return true;
        }

        bool loadPunctuationWords(const Value & value, std::vector<std::u16string> & punctuation_words)
        {
            if (value.type() != ValueType::Array)
                return false;

            const std::shared_ptr<Array> array = value.getArray();

            for(const Value & v : array->values()) {
                if (v.type() != ValueType::String)
                    return false;

                punctuation_words.push_back(v.getString());
            }

            return true;
        }

    }


    bool loadLexicalParameters(const std::string &file_name, inout::LexicalParameters &lex)
    {
        inout::NullReporter null_reporter;
        Value               value;
        JsonRdp             parser(null_reporter, file_name, value);

        bool                ok = parser.parse();

        if (!ok)
            return false;

        if (value.type() != ValueType::Object)
            return false;

        const std::shared_ptr<Object> lex_object = value.getObject();

        try
        {
            if (lex_object->exists(u"markups") && !loadMarkups(lex_object->find(u"markups"), lex.markups))
                return false;

            if (lex_object->exists(u"masks") && !loadMasks(lex_object->find(u"masks"), lex.masks))
                return false;

            if (lex_object->exists(u"punctuation_chars"))
                lex.punctuation_chars = lex_object->find(u"punctuation_chars").getString();

            if (lex_object->exists(u"punctuation_words") && !loadPunctuationWords(lex_object->find(u"punctuation_words"), lex.punctuation_words))
                return false;

            if (lex_object->exists(u"digits"))
                lex.digits = lex_object->find(u"digits").getString();

            if (lex_object->exists(u"latin_alphabet"))
                lex.latin_alphabet = lex_object->find(u"latin_alphabet").getString();

            if (lex_object->exists(u"national_alphabet"))
                lex.national_alphabet = lex_object->find(u"national_alphabet").getString();

            if (lex_object->exists(u"id_extra_symbols"))
                lex.id_extra_symbols = lex_object->find(u"id_extra_symbols").getString();

            if (lex_object->exists(u"may_national_letters_use"))
                lex.may_national_letters_use = lex_object->find(u"may_national_letters_use").getBool();

            if (lex_object->exists(u"may_national_letters_mix"))
                lex.may_national_letters_mix = lex_object->find(u"may_national_letters_mix").getBool();

            if (lex_object->exists(u"is_case_sensitive"))
                lex.is_case_sensitive = lex_object->find(u"is_case_sensitive").getBool();

            if (lex_object->exists(u"eof_symbol"))
                lex.eof_symbol = lex_object->find(u"eof_symbol").getString();

            if (lex_object->exists(u"nl_substitution"))
                lex.nl_substitution = lex_object->find(u"nl_substitution").getString();
        }
        catch(const std::exception &)
        {
            return false;
        }

        return true;
    }
}